(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('sendbird')) :
  typeof define === 'function' && define.amd ? define(['exports', 'rxjs', 'sendbird'], factory) :
  (global = global || self, factory(global.CommunicationLibrary = {}, global.rxjs, global.SendBird));
}(this, function (exports, rxjs, SendBird) { 'use strict';

  SendBird = SendBird && SendBird.hasOwnProperty('default') ? SendBird['default'] : SendBird;

  class Util {
      /**
       * Adding a library to the runtime global scope
       * addGlobalScripts
       * @param scriptsConfig IScript[]
       * @param callback once it ready.
       */
      static addGlobalScripts(scriptsConfig, callback) {
          // TODO： we need to support multiple scripts in the future
          if (scriptsConfig.length > 0 &&
              typeof document !== 'undefined' &&
              scriptsConfig[0].url &&
              scriptsConfig[0].url.trim()) {
              const script = document.createElement('script');
              script.src = scriptsConfig[0].url;
              script.onload = callback;
              document.body.appendChild(script);
          }
          else {
              callback();
          }
      }
      static convertToJsonObject(object) {
          if (!object) {
              return null;
          }
          try {
              JSON.parse(object);
          }
          catch (e) {
              return null;
          }
      }
  }

  class Contact {
      constructor(id, device, rawContact) {
          this.id = id;
          this.device = device;
          this.rawContact = rawContact;
          this.users = [];
          this.onNewEvent$ = new rxjs.Subject();
          this.onError$ = new rxjs.Subject();
          //
      }
      getId() {
          return this.id;
      }
      getFromAddress() {
          return this.fromAddress;
      }
      getState() {
          return this.state;
      }
      getDevice() {
          return this.device;
      }
      getCreatedAt() {
          return this.createdAt;
      }
      getCreatedBy() {
          return this.createdBy;
      }
      getEndedAt() {
          return this.endedAt;
      }
      getEndedBy() {
          return this.endedBy;
      }
      getConnectedAt() {
          return this.connectedAt;
      }
      getQueueName() {
          return this.queueName;
      }
      getUsers() {
          return this.users;
      }
  }

  class AWSContact extends Contact {
      // Also record aws device directly to call aws specified API
      constructor(awsContact, awsdevice, initializeState) {
          super(awsContact.getContactId(), awsdevice, awsContact);
          if (initializeState) {
              this.state = initializeState;
          }
          this.subscriptContactEvents(awsContact);
          this.initializeProperties(awsContact);
      }
      accept() {
          this.rawContact.accept({
              success: () => {
                  console.log('Accept contact successfully - server received your cmd');
              },
              failure: () => {
                  console.error('call awscontact.accept failed');
                  this.onError$.next({
                      type: 30003 /* JOIN_CONTACT_FAILED */,
                      message: 'call awscontact.accept failed',
                      payload: this
                  });
              }
          });
          return this;
      }
      getDirection() {
          return this.rawContact.isInbound()
              ? 1 /* INBOUND */
              : 2 /* OUTBOUND */;
      }
      decline() {
          this.hangup();
      }
      mute() {
          this.device.mute();
          this.addState(10006 /* MUTE */);
          return this;
      }
      unMute() {
          this.device.unmute();
          this.removeState(10006 /* MUTE */);
          return this;
      }
      hold() {
          this.rawContact.getInitialConnection().hold({
              success: () => {
                  this.state = this.addState(10005 /* HOLD */);
                  this.onNewEvent$.next({
                      eventType: 10011 /* CHANGE */,
                      payload: this
                  });
              },
              failure: () => {
                  console.error('hold failed');
                  this.onError$.next({
                      type: 30008 /* HOLD_FAILED */,
                      message: 'call awscontact.hold failed',
                      payload: this
                  });
              }
          });
          return this;
      }
      unHold() {
          this.rawContact.getInitialConnection().resume({
              success: () => {
                  this.state = this.removeState(10005 /* HOLD */);
                  this.onNewEvent$.next({
                      eventType: 10011 /* CHANGE */,
                      payload: this
                  });
              },
              failure: () => {
                  console.error('resume failed');
                  this.onError$.next({
                      type: 30009 /* RESUME_FAILED */,
                      message: 'call awscontact.resume failed',
                      payload: this
                  });
              }
          });
          return this;
      }
      hangup() {
          this.rawContact.getAgentConnection().destroy({
              success: () => {
                  this.endedBy = 'agent';
                  console.log('hangup is executed');
              },
              failure: () => {
                  console.error('failed');
                  this.onError$.next({
                      type: 30004 /* HANGUP_FAILED */,
                      message: 'call awscontact.destroy failed',
                      payload: this
                  });
              }
          });
          return this;
      }
      sendMessage(_messageParams) {
          //
      }
      getPreviousMessages(_params) {
          return new Promise(resolve => {
              resolve();
          });
      }
      typing(_parameters) {
          //
      }
      addState(addedState) {
          switch (addedState) {
              case 10005 /* HOLD */:
                  this.state =
                      this.state === 10006 /* MUTE */
                          ? 10007 /* HOLDANDMUTE */
                          : 10005 /* HOLD */;
                  break;
              case 10006 /* MUTE */:
                  this.state =
                      this.state === 10005 /* HOLD */
                          ? 10007 /* HOLDANDMUTE */
                          : 10006 /* MUTE */;
                  break;
              default:
                  break;
          }
          return this.state;
      }
      removeState(removedState) {
          switch (removedState) {
              case 10005 /* HOLD */:
                  this.state =
                      this.state === 10007 /* HOLDANDMUTE */
                          ? 10006 /* MUTE */
                          : 10003 /* CONNECTED */;
                  break;
              case 10006 /* MUTE */:
                  this.state =
                      this.state === 10007 /* HOLDANDMUTE */
                          ? 10005 /* HOLD */
                          : 10003 /* CONNECTED */;
                  break;
              default:
                  break;
          }
          return this.state;
      }
      subscriptContactEvents(contact) {
          contact.onConnected((newcontact) => {
              if (newcontact.getContactId() !== this.id) {
                  return;
              }
              this.connectedAt = new Date();
              this.state = 10002 /* ACCEPTED */;
              this.onNewEvent$.next({
                  eventType: 10011 /* CHANGE */,
                  payload: this
              });
              this.state = 10003 /* CONNECTED */;
              this.onNewEvent$.next({
                  eventType: 10011 /* CHANGE */,
                  payload: this
              });
          });
          contact.onEnded((newcontact) => {
              if (newcontact.getContactId() !== this.id) {
                  return;
              }
              this.endedAt = new Date();
              this.state = 10008 /* ENDED */;
              this.onNewEvent$.next({
                  eventType: 10011 /* CHANGE */,
                  payload: this
              });
          });
          contact.onMissed((newcontact) => {
              if (newcontact.getContactId() !== this.id) {
                  return;
              }
              this.state = 10008 /* ENDED */;
              this.onNewEvent$.next({
                  eventType: 10011 /* CHANGE */,
                  payload: this
              });
          });
      }
      initializeProperties(awsContact) {
          this.queueName = awsContact.getQueue().name || 'unknown queue';
          if (awsContact.getActiveInitialConnection()) {
              this.fromAddress = awsContact
                  .getActiveInitialConnection()
                  .getEndpoint().phoneNumber;
              this.createdAt = awsContact
                  .getActiveInitialConnection()
                  .getStatus().timestamp;
          }
          this.createdBy =
              awsContact.getActiveInitialConnection().getType() === 'inbound'
                  ? this.fromAddress
                  : 'agent';
      }
  }

  class Device {
      constructor(id, commlib) {
          this.id = id;
          this.commlib = commlib;
          this.contacts = [];
          this.onNewEvent$ = new rxjs.Subject();
          this.onError$ = new rxjs.Subject();
      }
      getId() {
          return this.id;
      }
      getStatus() {
          return this.status;
      }
      getState() {
          return this.state;
      }
      getContacts() {
          return this.contacts;
      }
      getType() {
          return this.type;
      }
      getAddress() {
          return this.address;
      }
      getName() {
          return this.name;
      }
      getUser() {
          return this.user;
      }
      getAdditionalData() {
          return this.additionalData;
      }
  }

  class AWSConnectAdditionalData {
      getUnassignContacts() {
          return new Promise(resolve => {
              resolve([]);
          });
      }
      deleteMessage(_contactId, _messageId) {
          return new Promise(resolve => {
              resolve(true);
          });
      }
  }

  class AWSDevice extends Device {
      constructor(deviceParameters, commlib) {
          super(deviceParameters.id || 'SAP-AWS-CONNECT', commlib);
          this.dependenciesLoaded = false;
          this.dependenciesSubject = new rxjs.Subject();
          this.expect_connected = false;
          Util.addGlobalScripts(deviceParameters.globalScripts, this.loaded.bind(this));
          this.ccpURL = deviceParameters && deviceParameters.ccpURL;
          this.setParameters();
      }
      static init(deviceParameters, commlib) {
          return new AWSDevice(deviceParameters, commlib);
      }
      // For AWS, we mute/unmute in agent level, provide public method to let contact call */
      mute() {
          this.agent.mute();
          return this;
      }
      unmute() {
          this.agent.unmute();
          return this;
      }
      getStatus() {
          return this.status;
      }
      register(parameters) {
          this.expect_connected = true;
          if (this.state === 3 /* OFFLINE */) {
              this.changeStateAndNotify(2 /* CONNECTED */);
              return;
          }
          // If not loaded, just wait and load again
          if (!this.dependenciesLoaded ||
              typeof connect === 'undefined' ||
              connect === null) {
              this.dependenciesSubject.subscribe(() => this.register(parameters));
              return;
          }
          this.ccpURL = (parameters && parameters.ccpURL) || this.ccpURL;
          if (!this.ccpURL) {
              console.error('register is called');
              return;
          }
          /***
           * Start to connect to aws ccp
           * TODO: more checkings for URL? Whether we need check whether we have connected?
           */
          const mgr = connect.core.getPopupManager();
          if (mgr) {
              mgr.clear(connect.MasterTopics.LOGIN_POPUP);
          }
          this.ccpDiv = parameters && parameters.ccpDiv;
          if (!this.ccpDiv && typeof document !== 'undefined') {
              this.ccpDiv = document.createElement('awsConnect');
              document.body.appendChild(this.ccpDiv);
              this.ccpDiv.style.visibility = 'hidden';
          }
          this.state = 4 /* DISCONNECTED */;
          connect.core.initCCP(this.ccpDiv, {
              ccpUrl: this.ccpURL,
              loginPopup: true,
              softphone: {
                  allowFramedSoftphone: true
              }
          });
          connect.agent(this.subscribeAgentEvent.bind(this));
          connect.contact(this.subscribeContactEvent.bind(this));
      }
      unregister() {
          console.log('unregister is called');
          this.expect_connected = false;
          this.changeStateAndNotify(4 /* DISCONNECTED */);
          this.setOffline();
      }
      setState(state) {
          if (this.state !== state) {
              switch (state) {
                  case 5 /* NOTROUTABLE */:
                      this.setAvailable(false);
                      break;
                  case 6 /* ROUTABLE */:
                      this.setAvailable(true);
                      break;
                  case 3 /* OFFLINE */:
                      this.setOffline();
                      break;
                  default:
                      console.error('setStte only could be used for routable/offline property');
                      break;
              }
          }
      }
      createContact(_parameters) {
          return new Promise((resolve, reject) => {
              if (!this.agent) {
                  reject(new Error(`Invalid agent`));
              }
              else {
                  resolve(null);
              }
          });
      }
      joinContact(contactId) {
          return new Promise((resolve, reject) => {
              if (!this.agent || !contactId) {
                  reject(new Error(`Invalid parameters`));
              }
              else {
                  resolve(null);
              }
          });
      }
      mapState(agentState) {
          let state;
          switch (agentState.type) {
              case connect.AgentStateType.ROUTABLE:
                  state = 6 /* ROUTABLE */;
                  break;
              case connect.AgentStateType.NOT_ROUTABLE:
                  state = 5 /* NOTROUTABLE */;
                  break;
              case connect.AgentStateType.OFFLINE:
                  state = 3 /* OFFLINE */;
                  break;
              default:
                  state = 5 /* NOTROUTABLE */;
                  break;
          }
          return state;
      }
      onError(agent) {
          console.error('One error raised by aws');
          this.onError$.next({
              type: 20009 /* ERROR */,
              message: 'Error raised by aws with payload agent',
              payload: agent
          });
          this.changeStateAndNotify(5 /* NOTROUTABLE */);
      }
      onIncoming(oContact) {
          const newState = oContact.isConnected()
              ? 10003 /* CONNECTED */
              : 10001 /* INCOMING */;
          const newContact = new AWSContact(oContact, this, newState);
          this.contacts.push(newContact);
          this.onNewEvent$.next({
              eventType: 10001 /* NEW */,
              payload: newContact
          });
      }
      removeContactFromList(inputContact) {
          for (let i = 0; i < this.contacts.length; i++) {
              if (this.contacts[i].getId() === inputContact.getContactId()) {
                  this.contacts.splice(i, 1);
                  return;
              }
          }
      }
      setAvailable(available) {
          const routableState = this.agent.getAgentStates().filter((state) => {
              if (available) {
                  return state.type === connect.AgentStateType.ROUTABLE;
              }
              else {
                  return state.type === connect.AgentStateType.NOT_ROUTABLE;
              }
          })[0];
          this.agent.setState(routableState, {
              success: () => {
                  console.log('Set agent status to Available (routable) via Streams');
              },
              failure: () => {
                  console.log('Failed to set agent status to Available (routable) via Streams');
              }
          });
      }
      setOffline() {
          const offlineState = this.agent.getAgentStates().filter((state) => {
              return state.type === connect.AgentStateType.OFFLINE;
          })[0];
          if (offlineState) {
              this.agent.setState(offlineState, {
                  success: () => {
                      console.log('Set agent status to offline (routable) via Streams');
                  },
                  failure: () => {
                      console.log('Failed to set agent status to offline (routable) via Streams');
                  }
              });
          }
      }
      changeStateAndNotify(newState) {
          // Raise Connected if previous state is disconnected
          if (!this.expect_connected && newState === 3 /* OFFLINE */) ;
          else {
              if (newState !== 4 /* DISCONNECTED */) {
                  this.expect_connected = true;
                  this.state = 2 /* CONNECTED */;
                  this.onNewEvent$.next({
                      eventType: 3 /* CHANGE */,
                      payload: this
                  });
              }
          }
          this.state = newState;
          this.onNewEvent$.next({
              eventType: 3 /* CHANGE */,
              payload: this
          });
      }
      // Get the agent state and forward to parents if needed
      handleAgentRefresh(agent) {
          const newStatusString = agent.getStatus() && agent.getStatus().name;
          if (!newStatusString) {
              this.changeStateAndNotify(5 /* NOTROUTABLE */);
              return;
          }
          if (newStatusString !== this.status) {
              this.status = newStatusString;
              const newAWSState = agent.getStatus() || connect.AgentStateType.NOT_ROUTABLE;
              const newDeviceState = this.mapState(newAWSState);
              this.changeStateAndNotify(newDeviceState);
          }
      }
      onMuteChanged(obj) {
          console.log('new state for mute is ' + obj.muted);
      }
      subscribeAgentEvent(agent) {
          this.user = agent.getName() || this.name;
          this.name = this.user + '@AWS-connect';
          this.agent = agent;
          const w = window.open('', connect.MasterTopics.LOGIN_POPUP);
          if (w) {
              w.close();
          }
          agent.onRefresh(this.handleAgentRefresh.bind(this));
          agent.onError(this.onError.bind(this));
          // below are useless?
          agent.onMuteToggle(this.onMuteChanged.bind(this));
      }
      // only watch when event comes and die
      subscribeContactEvent(contact) {
          if (contact.getStatus().type === connect.ContactStateType.ERROR) {
              console.error('A error raised from AWS with status' + contact.getStatus());
              return;
          }
          if (contact.getActiveInitialConnection()) {
              if (contact.isInbound()) {
                  this.onIncoming(contact);
              }
          }
          else {
              contact.onIncoming(this.onIncoming.bind(this));
          }
          // remove from contacts, the contact will notify listener after update its inner state
          contact.onEnded(this.removeContactFromList.bind(this));
          contact.onMissed(this.removeContactFromList.bind(this));
      }
      setParameters() {
          this.type = 1 /* PHONE */;
          this.state = 4 /* DISCONNECTED */;
          this.user = 'unknown';
          this.additionalData = new AWSConnectAdditionalData();
      }
      loaded() {
          this.dependenciesLoaded = true;
          this.dependenciesSubject.next();
      }
  }

  class ConfigurationService {
      constructor() {
          this.deviceConfigurations = [
              {
                  id: 'SAP-AWS-CONNECT',
                  key: 'SAP-AWS-CONNECT',
                  name: 'SAP AWS Connect',
                  isActive: true,
                  type: 1 /* PREDEFINED */,
                  location: {
                      path: '../providers/aws-connect/aws-device-provider',
                      className: 'AWSDevice'
                  },
                  channels: [1 /* PHONE */],
                  parameters: {
                      ccpURL: 'https://core-grm.awsapps.com/connect/ccp',
                      globalScripts: [
                          {
                              url: 'https://cs-ngx-thirdparty-libs-v1.cfapps.sap.hana.ondemand.com/libs/aws/connect-streams-min.js'
                          }
                      ]
                  }
              },
              {
                  id: 'SENDBIRD',
                  key: 'SENDBIRD',
                  name: 'SendBird',
                  isActive: true,
                  type: 1 /* PREDEFINED */,
                  location: {
                      path: '../providers/sendbird/sendbird-device',
                      className: 'SendBirdDevice'
                  },
                  channels: [2 /* CHAT */],
                  parameters: {
                      appId: '1420A8B8-B95A-4EC3-972A-89C02089D239',
                      apiToken: 'ec0daad685702f7dcdb8a7c61d9225b1d38adcd4',
                      apiRequestUrl: 'https://api-1420A8B8-B95A-4EC3-972A-89C02089D239.sendbird.com'
                  }
              },
              {
                  id: 'SENDBIRD',
                  key: 'SENDBIRD',
                  name: 'SendBird',
                  isActive: false,
                  type: 2 /* DYMAMICLOAD */,
                  location: {
                      path: 'https://cs-ngx-thirdparty-libs-v1.cfapps.sap.hana.ondemand.com/devices/bundle.js',
                      className: 'SendBirdDevice'
                  },
                  channels: [2 /* CHAT */],
                  parameters: {
                      appId: '1420A8B8-B95A-4EC3-972A-89C02089D239',
                      apiToken: 'ec0daad685702f7dcdb8a7c61d9225b1d38adcd4',
                      apiRequestUrl: 'https://api-1420A8B8-B95A-4EC3-972A-89C02089D239.sendbird.com'
                  }
              }
          ];
          // initial things
      }
      getConfigurations() {
          // TODO: it should be refined after configuration service is ready.
          return this.deviceConfigurations;
      }
  }

  class SendBirdContact extends Contact {
      // Also record aws device directly to call aws specified API
      constructor(direction, rawContact, sendbirddevice, initializeState) {
          super(rawContact.url, sendbirddevice, rawContact);
          this.direction = direction;
          this.sendbirdInstance = null;
          this.sendbirdInstance = sendbirddevice.getSendBirdInstance();
          this.state = initializeState || 10009 /* UNKNOWN */;
          this.initContact();
          this.subscribeEvents();
      }
      accept() {
          this.rawContact.join((channel, error) => {
              if (error) {
                  this.onError$.next({
                      type: 30003 /* JOIN_CONTACT_FAILED */,
                      message: `Join contact ${channel.name} failed.`,
                      payload: error
                  });
              }
              else {
                  this.onNewEvent$.next({
                      eventType: 10009 /* CONNECTED */,
                      payload: this
                  });
              }
          });
          return this;
      }
      getDirection() {
          return this.direction;
      }
      decline() {
          return this;
      }
      mute() {
          return this;
      }
      unMute() {
          return this;
      }
      hold() {
          return this;
      }
      unHold() {
          return this;
      }
      hangup() {
          if (!this.sendbirdInstance || !this.rawContact) {
              this.onError$.next({
                  type: 30005 /* CONNECT_FAILED */,
                  message: 'Please register SendBird device first.',
                  payload: 'No sendbird connnection'
              });
              return;
          }
          this.rawContact.leave((_response, error) => {
              if (error) {
                  this.onError$.next({
                      type: 30004 /* HANGUP_FAILED */,
                      message: 'Hangup contact failed from sendbird',
                      payload: error
                  });
                  return;
              }
              this.state = 10008 /* ENDED */;
              this.onNewEvent$.next({
                  eventType: 10010 /* DISCONNECTED */,
                  payload: this
              });
          });
          return this;
      }
      sendMessage(msgParams) {
          if (!msgParams || (!msgParams.messageContent && !msgParams.data)) {
              this.onError$.next({
                  type: 30006 /* INVALID_MSG */,
                  message: 'Please enter message or data you want to send.',
                  payload: 'Invalid message'
              });
              return;
          }
          if (!this.sendbirdInstance || !this.rawContact) {
              this.onError$.next({
                  type: 30005 /* CONNECT_FAILED */,
                  message: 'Please register SendBird device first.',
                  payload: 'No sendbird connnection'
              });
          }
          else {
              this.rawContact.sendUserMessage(msgParams.messageContent, msgParams.data, msgParams.msgCustomType, (msg, error) => {
                  if (error) {
                      this.onError$.next({
                          type: 30001 /* SEND_MSG_FAILED */,
                          message: 'Send message failed.',
                          payload: error
                      });
                  }
                  else {
                      this.rawContact.endTyping();
                      this.onNewEvent$.next({
                          eventType: 10002 /* NEW_MESSAGE */,
                          payload: this.convertMsg(msg)
                      });
                  }
              });
          }
      }
      typing(isTyping) {
          if (!this.sendbirdInstance || !this.rawContact) {
              return;
          }
          if (isTyping) {
              this.rawContact.startTyping();
          }
          else {
              this.rawContact.endTyping();
          }
      }
      getPreviousMessages(parameters) {
          if (!this.sendbirdInstance || !this.rawContact) {
              this.onError$.next({
                  type: 30005 /* CONNECT_FAILED */,
                  message: 'Please register SendBird device first.',
                  payload: 'No sendbird connnection'
              });
              return;
          }
          let messageLimit;
          if (this.prevMessagesQuery === null || parameters.forceNewQuery) {
              this.prevMessagesQuery = this.rawContact.createPreviousMessageListQuery();
              this.prevMessagesQuery.limit = parameters.limit || 20;
              this.prevMessagesQuery.reverse = parameters.reverse || false;
              messageLimit = parameters.limit || 20;
          }
          else {
              messageLimit = this.prevMessagesQuery.limit;
          }
          this.prevMessagesQuery.load((messages, error) => {
              if (error) {
                  this.onError$.next({
                      type: 30007 /* LOAD_MSGS_FAILED */,
                      message: 'Retrieve messages failed',
                      payload: error
                  });
                  return;
              }
              if (messages.length === messageLimit) {
                  this.onNewEvent$.next({
                      eventType: 10005 /* MESSAGES_MORE */,
                      payload: true
                  });
              }
              else {
                  this.onNewEvent$.next({
                      eventType: 10005 /* MESSAGES_MORE */,
                      payload: false
                  });
              }
              this.rawContact.markAsRead();
              const prevMsgs = messages.map((msg) => {
                  return this.convertMsg(msg);
              });
              this.onNewEvent$.next({
                  eventType: 10012 /* MESSAGES_LOADED */,
                  payload: prevMsgs
              });
          });
      }
      initContact() {
          if (!this.rawContact) {
              return;
          }
          this.queueName = this.rawContact.name || 'unknown';
          this.fromAddress = this.rawContact.inviter
              ? this.rawContact.inviter.userId || 'unknow'
              : 'unknown';
          this.createdAt = this.rawContact.createdAt || new Date().getTime();
          this.createdBy = this.fromAddress;
      }
      subscribeEvents() {
          if (!this.rawContact || !this.device) {
              return;
          }
          const msgReceivedHandler = new this.sendbirdInstance.ChannelHandler();
          msgReceivedHandler.onMessageReceived = this.onMsgRecieved.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_MESSAGE_RECEIVED_HANDLER_ID', msgReceivedHandler);
          const typingHandler = new this.sendbirdInstance.ChannelHandler();
          typingHandler.onTypingStatusUpdated = this.onTypingUpdated.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_TYPING_INDICATOR_HANDLER_ID', typingHandler);
          const msgUpdatedHandler = new this.sendbirdInstance.ChannelHandler();
          msgUpdatedHandler.onMessageUpdated = this.onMsgUpdated.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_MESSAGE_UPDATED_HANDLER_ID', msgUpdatedHandler);
          const userJoinedHandler = new this.sendbirdInstance.ChannelHandler();
          userJoinedHandler.onUserJoined = this.onUserJoined.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_USER_JOINED_HANDLER_ID', userJoinedHandler);
          const userLeftHandler = new this.sendbirdInstance.ChannelHandler();
          userLeftHandler.onUserLeft = this.onUserLeft.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_USER_LEFT_HANDLER_ID', userLeftHandler);
          const msgDeletedHandler = new this.sendbirdInstance.ChannelHandler();
          msgDeletedHandler.onMessageDeleted = this.onMessageDeleted.bind(this);
          this.sendbirdInstance.addChannelHandler('CS_COMMLIB_MESSAGE_DELETED_HANDLER_ID', msgDeletedHandler);
      }
      onMsgRecieved(channel, msg) {
          if (!this.rawContact || channel.url !== this.rawContact.url) {
              return;
          }
          const newMsg = this.convertMsg(msg);
          this.onNewEvent$.next({
              eventType: 10002 /* NEW_MESSAGE */,
              payload: newMsg
          });
      }
      onTypingUpdated(channel) {
          if (channel.isTyping()) {
              this.onNewEvent$.next({
                  eventType: 10006 /* TYPING */,
                  payload: true
              });
          }
          else {
              this.onNewEvent$.next({
                  eventType: 10006 /* TYPING */,
                  payload: false
              });
          }
      }
      onMsgUpdated(channel, msg) {
          if (!this.rawContact || channel.url !== this.rawContact.url) {
              return;
          }
          const newMsg = this.convertMsg(msg);
          this.onNewEvent$.next({
              eventType: 10003 /* MESSAGE_MODIFIED */,
              payload: newMsg
          });
      }
      onMessageDeleted(channel, messageId) {
          if (!this.rawContact || channel.url !== this.rawContact.url) {
              return;
          }
          this.onNewEvent$.next({
              eventType: 10004 /* MESSAGE_REMOVED */,
              payload: messageId
          });
      }
      onUserJoined(channel, user) {
          if (!this.rawContact || channel.url !== this.rawContact.url) {
              return;
          }
          const newUser = user;
          this.onNewEvent$.next({
              eventType: 10007 /* USER_JOINED */,
              payload: newUser
          });
      }
      onUserLeft(channel, user) {
          if (!this.rawContact || channel.url !== this.rawContact.url) {
              return;
          }
          const newUser = user;
          this.onNewEvent$.next({
              eventType: 10008 /* USER_LEFT */,
              payload: newUser
          });
      }
      convertMsg(message) {
          const newMessage = {
              id: message.messageId,
              contactId: message.channelUrl,
              index: new Date().getTime(),
              content: message.message,
              createdAt: message.createdAt,
              updatedAt: message.updatedAt,
              format: message.messageType,
              type: 1 /* MESSAGE */,
              sender: message.sender.userId,
              additionalProperties: Util.convertToJsonObject(message.data)
          };
          if (message.isFileMessage && message.isFileMessage()) {
              const fileMessage = message;
              newMessage.additionalProperties.thumbnail =
                  fileMessage.thumbnails && fileMessage.thumbnails.length > 0
                      ? fileMessage.thumbnails[0]
                      : null;
          }
          return newMessage;
      }
  }

  // import * as request from 'request';
  class SendBirdAdditionalData {
      constructor(sendbirdInstance, parameters) {
          this.sendbirdInstance = sendbirdInstance;
          this.apiToken = parameters.apiToken || '';
          this.apiUrl = parameters.apiRequestUrl || '';
      }
      getUnassignContacts() {
          return new Promise((resolve, reject) => {
              if (!this.sendbirdInstance) {
                  reject('There is no connection to sendbird.');
                  return;
              }
              if (!this.apiUrl || !this.apiToken) {
                  reject('The apiRequestUrl or apiToken is not provided.');
                  return;
              }
              this.unassignedChannelsQuery = this.sendbirdInstance.GroupChannel.createMyGroupChannelListQuery();
              this.unassignedChannelsQuery.limit = 20;
              this.unassignedChannelsQuery.includeEmpty = true;
              this.unassignedChannelsQuery.customTypeFilter = 'unassigned';
              if (this.unassignedChannelsQuery.hasNext) {
                  this.unassignedChannelsQuery.next((groupChannels, error) => {
                      if (error) {
                          reject(`Retrieve unassigned contacts failed with error: ${error}`);
                          return;
                      }
                      const contacts = groupChannels.map((channel) => {
                          const contact = {
                              id: channel.url,
                              name: channel.name,
                              state: 'unassigned',
                              createdAt: channel.createdAt,
                              createdBy: channel.inviter.userId
                          };
                          return contact;
                      });
                      resolve(contacts);
                  });
              }
          });
      }
      deleteMessage(contactId, messageId) {
          return new Promise((resolve, reject) => {
              if (!this.apiUrl || !this.apiToken) {
                  reject('The apiRequestUrl or apiToken is not provided.');
                  return;
              }
              if (!contactId || !messageId) {
                  reject('The contactId and messageId are mandatory.');
                  return;
              }
              resolve(true);
              /* const httpOptions = {
                url: `${this.apiUrl}/group_channels/${contactId}/messages/${messageId}`,
                headers: {
                  'Content-Type': 'application/json, charset=utf8',
                  'Api-Token': this.apiToken
                }
              }; */
              /* request.delete(httpOptions, (error, response) => {
                if (error) {
                  reject(`[${response.statusCode}] ${error}`);
                } else {
                  resolve(true);
                }
              }); */
          });
      }
  }

  class SendBirdDevice extends Device {
      constructor(parameters, commlib) {
          super(parameters.appId || 'SAP-SENDBIRD-DEVICE', commlib);
          this.sendbirdInstance = null;
          this.sendbirdInstance = this.sendbirdInstance
              ? this.sendbirdInstance
              : new SendBird({ appId: parameters.appId });
          this.type = 2 /* CHAT */;
          this.state = 4 /* DISCONNECTED */;
          this.additionalData = new SendBirdAdditionalData(this.sendbirdInstance, parameters);
          this.subscribeEvents();
      }
      static init(deviceParameters, commlib) {
          return new SendBirdDevice(deviceParameters, commlib);
      }
      /**
       * Connect to sendbird server with an unique ID
       * @param userId the ID should be unique within a SendBird application to be distinguished from each other
       * @anotherNote Fex. a hashed email address or phone number in your service.
       */
      register(parameters) {
          if (!parameters || !parameters.userId) {
              console.error('Invalid userId, the userId is mandatory.');
              return;
          }
          this.user = `${parameters.userId}`; // convert to string
          this.changeState(1 /* CONNECTING */);
          this.sendbirdInstance.connect(this.user, (_user, error) => {
              if (error) {
                  this.errorHandler(20007 /* REGISTER_FAILED */, 'Connect to sendbird failed.', error);
                  this.changeState(4 /* DISCONNECTED */);
                  return;
              }
              this.name = `SendBird-Device-${this.user}`;
              this.changeState(2 /* CONNECTED */);
          });
      }
      /**
       * Disconnect current device, that means user will not be able to join chat channel
       */
      unregister() {
          if (!this.user) {
              console.error('Please register device first');
              return;
          }
          this.sendbirdInstance.disconnect(() => {
              console.log('Sendbird device is disconnected');
              this.user = '';
              this.changeState(4 /* DISCONNECTED */);
          });
      }
      /**
       *
       * @param state The device state you want to set
       */
      setState(state) {
          if (this.state === state) {
              console.log('State must different with before.');
              return;
          }
          if (!this.user) {
              console.error('Please register device first.');
              return;
          }
          switch (state) {
              case 5 /* NOTROUTABLE */:
                  this.register(this.user);
                  break;
              case 6 /* ROUTABLE */:
                  this.unregister();
                  break;
              default:
                  break;
          }
      }
      createContact(parameters) {
          return new Promise((resolve, reject) => {
              if (!this.sendbirdInstance || !this.user) {
                  reject(new Error('Please register SendBird device first.'));
              }
              else {
                  const params = new this.sendbirdInstance.GroupChannelParams();
                  params.isPublic = Boolean(parameters.isPublic);
                  params.isDistinct = Boolean(parameters.isDistinct);
                  params.addUserIds([this.user]);
                  params.name = parameters.name || '';
                  this.sendbirdInstance.GroupChannel.createChannel(params, (groupChannel, error) => {
                      if (error) {
                          reject(new Error(`Create contact failed with error: ${error}`));
                      }
                      else {
                          const newContact = new SendBirdContact(2 /* OUTBOUND */, groupChannel, this, 10004 /* CONNECTING */);
                          this.contacts.push(newContact);
                          this.onNewEvent$.next({
                              eventType: 10001 /* NEW */,
                              payload: newContact
                          });
                          resolve(newContact);
                      }
                  });
              }
          });
      }
      joinContact(contactId) {
          return new Promise((resolve, reject) => {
              if (!this.sendbirdInstance || !this.user) {
                  reject(new Error(`Please register SendBird device first.`));
              }
              else {
                  this.sendbirdInstance.GroupChannel.getChannel(contactId, (groupChannel, error1) => {
                      if (error1) {
                          reject(new Error(`Search contact failed with error: ${error1}`));
                      }
                      else {
                          const newContact = new SendBirdContact(1 /* INBOUND */, groupChannel, this, 10003 /* CONNECTED */);
                          this.contacts.push(newContact);
                          this.onNewEvent$.next({
                              eventType: 10009 /* CONNECTED */,
                              payload: newContact
                          });
                          resolve(newContact);
                      }
                  });
              }
          });
      }
      getSendBirdInstance() {
          return this.sendbirdInstance;
      }
      changeState(newState) {
          this.state = newState;
          this.onNewEvent$.next({
              eventType: 3 /* CHANGE */,
              payload: this
          });
      }
      subscribeEvents() {
          const reconnectSucHandler = new this.sendbirdInstance.ConnectionHandler();
          reconnectSucHandler.onReconnectSucceeded = this.onReconnectSuc.bind(this);
          this.sendbirdInstance.addConnectionHandler('CS_COMMLIB_RECONNECT_STARTED_HANDLER_ID', reconnectSucHandler);
      }
      onReconnectSuc() {
          this.onNewEvent$.next({
              eventType: 2 /* RECONNECTED */,
              payload: this
          });
      }
      errorHandler(type, msg, error) {
          if (!error || !msg) {
              return;
          }
          console.error(msg);
          this.onError$.next({
              type: type,
              message: msg,
              payload: error
          });
      }
  }

  class CommunicationLibrary {
      constructor() {
          this.devices = [];
          this.userState = 1 /* UNKNOWN */;
          this.onError$ = new rxjs.Subject();
          this.onNewEvent$ = new rxjs.Subject();
          this.configuration = new ConfigurationService();
          const aDeviceConfigurations = this.configuration.getConfigurations();
          for (const deviceCfg of aDeviceConfigurations) {
              if (deviceCfg.isActive) {
                  if (deviceCfg.type === 1 /* PREDEFINED */) {
                      switch (deviceCfg.key) {
                          case 'SAP-AWS-CONNECT':
                              try {
                                  const oProvider = AWSDevice.init(deviceCfg.parameters, this);
                                  this.devices.push(oProvider);
                              }
                              catch (e) {
                                  this.onError$.next({
                                      type: 10001 /* INITIAL_FAILED */,
                                      payload: e
                                  });
                              }
                              break;
                          case 'SENDBIRD':
                              try {
                                  const oProvider = SendBirdDevice.init(deviceCfg.parameters, this);
                                  this.devices.push(oProvider);
                              }
                              catch (e) {
                                  this.onError$.next({
                                      type: 10001 /* INITIAL_FAILED */,
                                      payload: e
                                  });
                              }
                              break;
                          default:
                              console.error('unknow predefined provider is found');
                      }
                  }
                  else {
                      this.dynamicLoadDevices(deviceCfg);
                  }
              }
          }
      }
      static getCommlib() {
          if (!this.instance) {
              this.instance = new CommunicationLibrary();
          }
          return this.instance;
      }
      getDevices() {
          return this.devices;
      }
      getDeviceById(id) {
          return this.devices.filter(item => id === item.getId())[0];
      }
      getDeviceByType(channelType) {
          return this.devices.filter(item => channelType === item.getType())[0];
      }
      getUserState() {
          return this.userState;
      }
      setUserState(newState) {
          switch (newState) {
              case 2 /* AVAILABLE */:
                  for (const oDevice of this.devices) {
                      if (oDevice.getState() !== 2 /* CONNECTED */) {
                          oDevice.register();
                      }
                  }
                  break;
              case 3 /* UNAVAILABLE */:
                  // Call unregister even the device state is not connected;
                  for (const oDevice of this.devices) {
                      oDevice.unregister();
                  }
                  break;
              case 1 /* UNKNOWN */:
              case 4 /* BUSY */:
                  break;
              default:
                  //
                  break;
          }
      }
      dynamicLoadDevices(deviceCfg) {
          if (!deviceCfg ||
              !deviceCfg.location ||
              !deviceCfg.location.path ||
              !deviceCfg.location.className) {
              return;
          }
          this.importModule(deviceCfg.location.path)
              .then(device => {
              if (!device || typeof device.init !== 'function') {
                  return;
              }
              this.devices.push(device.init(deviceCfg, this));
          })
              .catch(e => {
              console.error(`Dynamic loading device with error: ${e}`);
          });
      }
      importModule(url) {
          return new Promise((resolve, reject) => {
              const script = document.createElement('script');
              const tempGlobal = '__tempDeviceModule' +
                  Math.random()
                      .toString(32)
                      .substring(2);
              script.type = 'module';
              script.textContent = `import * as deviceModule from "${url}"; window.${tempGlobal} = deviceModule;`;
              script.onload = () => {
                  resolve(window[tempGlobal]);
                  // delete window[tempGlobal];
                  // script.remove();
              };
              script.onerror = () => {
                  reject(new Error('Failed to load module script with URL ' + url));
                  // delete window[tempGlobal];
                  // script.remove();
              };
              document.documentElement.appendChild(script);
          });
      }
  }

  exports.CommunicationLibrary = CommunicationLibrary;
  exports.Contact = Contact;
  exports.Device = Device;

  Object.defineProperty(exports, '__esModule', { value: true });

}));
